/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017-07-14     aubr.cool    1st version
 */
#include <rtthread.h>
#include <rtdevice.h>

struct fm24clxx_config
{
    rt_uint32_t size;
    rt_uint16_t addr;
    rt_uint16_t flags;
};

struct fm24clxx_device
{
    struct rt_device parent;
    struct rt_i2c_bus_device *bus;
};

#ifdef RT_USING_FM24CL64
static struct fm24clxx_device fm24clxx_drv;

static struct fm24clxx_config fm24cl64_config = {
    .size = 8192,
    .addr = (0xA0 >> 1),
    .flags = 0,
};
#endif

/* RT-Thread device interface */

static rt_err_t fm24clxx_register(const char *e2m_device_name,
                                  const char *i2c_bus, void *user_data);

static rt_err_t fm24clxx_init(rt_device_t dev)
{
    return RT_EOK;
}
static rt_err_t fm24clxx_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}

static rt_err_t fm24clxx_close(rt_device_t dev)
{
    return RT_EOK;
}

static rt_err_t fm24clxx_control(rt_device_t dev, int cmd, void *args)
{
    return RT_EOK;
}

static rt_size_t fm24clxx_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    struct fm24clxx_device *fm24clxx;
    const struct fm24clxx_config *cfg;
    struct rt_i2c_msg msg[2];
    rt_uint8_t mem_addr[2] = {
        0,
    };
    rt_size_t ret = 0;
    RT_ASSERT(dev != 0);

    fm24clxx = (struct fm24clxx_device *)dev;

    RT_ASSERT(fm24clxx->parent.user_data != 0);
    cfg = (const struct fm24clxx_config *)fm24clxx->parent.user_data;

    if (pos > cfg->size)
    {
        return 0;
    }

    if (pos + size > cfg->size)
    {
        size = cfg->size - pos;
    }

    msg[0].addr = cfg->addr;
    msg[0].flags = cfg->flags | RT_I2C_WR;
    mem_addr[0] = (pos >> 8);
    mem_addr[1] = (rt_uint8_t)pos;
    msg[0].buf = (rt_uint8_t *)mem_addr;
    msg[0].len = 2;

    msg[1].addr = cfg->addr;
    msg[1].flags = cfg->flags | RT_I2C_RD;
    msg[1].buf = (rt_uint8_t *)buffer;
    msg[1].len = size;

    ret = rt_i2c_transfer(fm24clxx->bus, msg, 2);
    return (ret == 2) ? size : 0;
}

static rt_size_t fm24clxx_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    struct fm24clxx_device *fm24clxx;
    const struct fm24clxx_config *cfg;
    struct rt_i2c_msg msg[2];
    rt_uint8_t mem_addr[2] = {
        0,
    };
    rt_size_t ret = 0;
    RT_ASSERT(dev != 0);

    fm24clxx = (struct fm24clxx_device *)dev;

    RT_ASSERT(fm24clxx->parent.user_data != 0);
    cfg = (const struct fm24clxx_config *)fm24clxx->parent.user_data;

    if (pos > cfg->size)
    {
        return 0;
    }

    if (pos + size > cfg->size)
    {
        size = cfg->size - pos;
    }

    msg[0].addr = cfg->addr;
    msg[0].flags = cfg->flags | RT_I2C_WR;
    mem_addr[0] = (pos >> 8);
    mem_addr[1] = (rt_uint8_t)pos;
    msg[0].buf = (rt_uint8_t *)mem_addr;
    msg[0].len = 2;

    msg[1].addr = cfg->addr;
    msg[1].flags = cfg->flags | RT_I2C_WR | RT_I2C_NO_START;
    msg[1].buf = (rt_uint8_t *)buffer;
    msg[1].len = size;

    ret = rt_i2c_transfer(fm24clxx->bus, msg, 2);
    return (ret == 2) ? size : 0;
}

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device fm24clxx_ops =
    {
        fm24clxx_init,
        fm24clxx_open,
        fm24clxx_close,
        fm24clxx_read,
        fm24clxx_write,
        fm24clxx_control};
#endif

rt_err_t fm24clxx_register(const char *fm_device_name, const char *i2c_bus, void *user_data)
{
    struct rt_i2c_bus_device *bus;

    bus = rt_i2c_bus_device_find(i2c_bus);
    if (bus == RT_NULL)
    {
        return RT_ENOSYS;
    }

    fm24clxx_drv.bus = bus;
    fm24clxx_drv.parent.type = RT_Device_Class_Block;
#ifdef RT_USING_DEVICE_OPS
    fm24clxx_drv.parent.ops = &fm24clxx_ops;
#else
    fm24clxx_drv.parent.init = fm24clxx_init;
    fm24clxx_drv.parent.open = fm24clxx_open;
    fm24clxx_drv.parent.close = fm24clxx_close;
    fm24clxx_drv.parent.read = fm24clxx_read;
    fm24clxx_drv.parent.write = fm24clxx_write;
    fm24clxx_drv.parent.control = fm24clxx_control;
#endif

    fm24clxx_drv.parent.user_data = user_data;

    return rt_device_register(&fm24clxx_drv.parent, fm_device_name, RT_DEVICE_FLAG_RDWR);
}

static int stm32_fm24clxx_attach(void)
{
    int result = 0;
#ifdef RT_USING_FM24CL64

    result = fm24clxx_register("fm24cl64", "i2c1_bus", &fm24cl64_config);
    if (RT_ENOSYS == result)
    {
        rt_kprintf("please check fm24clxx device\n");
    }
#endif
    return result;
}
INIT_DEVICE_EXPORT(stm32_fm24clxx_attach);
